package evolution.gui;

import java.awt.event.ActionEvent;
import java.util.Arrays;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextArea;

import com.jgoodies.binding.adapter.BasicComponentFactory;
import com.jgoodies.binding.adapter.BoundedRangeAdapter;
import com.jgoodies.binding.adapter.ComboBoxAdapter;
import com.jgoodies.binding.beans.BeanAdapter;
import com.jgoodies.binding.value.ConverterFactory;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

import evolution.algorithm.CoevolutionAlgorithm;
import evolution.data.CoevolutionAlgorithmFactory;
import evolution.data.CoevolutionDataBean;
import evolution.data.Initialization;
import evolution.data.Migration;
import evolution.data.Mutation;
import evolution.data.Problem;
import evolution.data.Reproduction;
import evolution.data.Selection;
import evolution.data.Stop;
import evolution.data.Succession;
import evolution.individual.Individual;
import evolution.problem.OptimizationProblem;

public class CoevolutionGUI extends JFrame {
	private static final long serialVersionUID = -5247997962303190779L;
	private static final String COL_SPECS = "pref:grow, 3dlu, pref:grow, 3dlu, pref:grow, 5dlu, pref:grow, 3dlu, pref:grow, 3dlu, pref:grow";
	private static final String ROW_SPECS = "";

	private CoevolutionDataBean coevolutionDataBean = new CoevolutionDataBean();

	private static CoevolutionGUI instance = null;

	public static CoevolutionGUI getInstance() {
		if (instance == null) {
			instance = new CoevolutionGUI();
		}

		return instance;
	}

	private CoevolutionGUI() {
		setTitle("CoEvo");

		BeanAdapter<CoevolutionDataBean> adapter = new BeanAdapter<>(
				coevolutionDataBean, true);

		FormLayout layout = new FormLayout(COL_SPECS, ROW_SPECS);
		layout.setColumnGroups(new int[][] { { 3, 9 }, { 5, 11 } });

		DefaultFormBuilder builder = new DefaultFormBuilder(layout);

		builder.appendSeparator("Problem optymalizacyjny");

		ValueModel problemModel = adapter.getValueModel("problem");
		JComboBox<Problem> problemComboBox = new JComboBox<>(
				new ComboBoxAdapter<Problem>(Problem.values(), problemModel));
		builder.append("Problem", problemComboBox, 3);

		ValueModel dimensionsModel = adapter.getValueModel("dimensions");
		JSlider dimensionsSlider = new JSlider(new BoundedRangeAdapter(
				dimensionsModel, 0, 1, 100));
		builder.append("Liczba wymiarów", dimensionsSlider);
		builder.append(BasicComponentFactory
				.createIntegerField(dimensionsModel));

		builder.appendSeparator("Parametry podstawowe");

		ValueModel populationsCountModel = adapter
				.getValueModel("populationsCount");
		JSlider populationsSlider = new JSlider(new BoundedRangeAdapter(
				populationsCountModel, 0, 1, 20));
		builder.append("Liczba populacji", populationsSlider);
		builder.append(BasicComponentFactory
				.createIntegerField(populationsCountModel));

		ValueModel populationSizeModel = adapter
				.getValueModel("populationSize");
		JSlider populationSizeSlider = new JSlider(new BoundedRangeAdapter(
				populationSizeModel, 0, 3, 100));
		builder.append("Rozmiar populacji", populationSizeSlider);
		builder.append(BasicComponentFactory
				.createIntegerField(populationSizeModel));

		builder.appendSeparator("Strategie algorytmu");

		ValueModel reproductionModel = adapter.getValueModel("reproduction");
		JComboBox<Reproduction> reproductionComboBox = new JComboBox<>(
				new ComboBoxAdapter<Reproduction>(Reproduction.values(),
						reproductionModel));
		builder.append("Krzyżowanie", reproductionComboBox);

		ValueModel reproductionProbabilityModel = adapter
				.getValueModel("reproductionProbability");
		ValueModel reproductionProbabilityPermillage = ConverterFactory
				.createDoubleToIntegerConverter(reproductionProbabilityModel,
						1000);
		builder.append(BasicComponentFactory
				.createIntegerField(reproductionProbabilityPermillage));

		ValueModel mutationModel = adapter.getValueModel("mutation");
		JComboBox<Mutation> mutationComboBox = new JComboBox<>(
				new ComboBoxAdapter<Mutation>(Mutation.values(), mutationModel));
		builder.append("Mutacja", mutationComboBox);

		ValueModel mutationProbabilityModel = adapter
				.getValueModel("mutationProbability");
		ValueModel mutationProbabilityPermillage = ConverterFactory
				.createDoubleToIntegerConverter(mutationProbabilityModel, 1000);
		builder.append(BasicComponentFactory
				.createIntegerField(mutationProbabilityPermillage));

		ValueModel selectionModel = adapter.getValueModel("selection");
		JComboBox<Selection> selectionComboBox = new JComboBox<>(
				new ComboBoxAdapter<Selection>(Selection.values(),
						selectionModel));
		builder.append("Selekcja", selectionComboBox, 3);

		ValueModel successionModel = adapter.getValueModel("succession");
		JComboBox<Succession> successionComboBox = new JComboBox<>(
				new ComboBoxAdapter<Succession>(Succession.values(),
						successionModel));
		builder.append("Sukcesja", successionComboBox, 3);

		ValueModel initializationModel = adapter
				.getValueModel("initialization");
		JComboBox<Initialization> initializationComboBox = new JComboBox<>(
				new ComboBoxAdapter<Initialization>(Initialization.values(),
						initializationModel));
		builder.append("Inicjalizacja", initializationComboBox, 3);

		ValueModel stopModel = adapter.getValueModel("stop");
		JComboBox<Stop> stopComboBox = new JComboBox<>(
				new ComboBoxAdapter<Stop>(Stop.values(), stopModel));
		builder.append("Warunek stopu", stopComboBox);

		ValueModel stopParameterModel = adapter.getValueModel("stopParameter");
		builder.append(BasicComponentFactory
				.createIntegerField(stopParameterModel));

		builder.appendSeparator("Koewolucja");

		ValueModel epochLengthModel = adapter.getValueModel("epochLength");
		JSlider epochLengthSlider = new JSlider(new BoundedRangeAdapter(
				epochLengthModel, 0, 1, 500));
		builder.append("Długość epoki", epochLengthSlider);
		builder.append(BasicComponentFactory
				.createIntegerField(epochLengthModel));

		ValueModel migrationModel = adapter.getValueModel("migration");
		JComboBox<Migration> migrationComboBox = new JComboBox<>(
				new ComboBoxAdapter<Migration>(Migration.values(),
						migrationModel));
		builder.append("Migracja", migrationComboBox, 3);

		ValueModel migrationParameterModel = adapter
				.getValueModel("migrationParameter");
		JSlider migrationParameterSlider = new JSlider(new BoundedRangeAdapter(
				migrationParameterModel, 0, 0, 20));
		builder.append("Rozmiar migracji", migrationParameterSlider);
		builder.append(BasicComponentFactory
				.createIntegerField(migrationParameterModel));

		builder.setDefaultDialogBorder();
		builder.nextLine();
		builder.appendSeparator();

		final JButton button = new JButton();
		builder.append(button);

		builder.nextLine();
		final JTextArea logArea = new JTextArea();
		logArea.setWrapStyleWord(true);
		logArea.setLineWrap(true);
		logArea.setEditable(false);
		logArea.setRows(12);
		builder.append(new JScrollPane(logArea), 11);

		getContentPane().add(builder.getPanel());
		getRootPane().setDefaultButton(button);

		button.setAction(new AbstractAction("Uruchom") {
			private static final long serialVersionUID = -622799403759824688L;

			@Override
			public void actionPerformed(ActionEvent e) {
				CoevolutionAlgorithmFactory coevolutionAlgorithmFactory = new CoevolutionAlgorithmFactory();
				CoevolutionAlgorithm algorithm = coevolutionAlgorithmFactory
						.create(coevolutionDataBean);
				algorithm.run();

				Individual bestIndividual = algorithm.getBestIndividual();

				logArea.setText("Współrzędne: "
						+ Arrays.toString(bestIndividual.getChromosome()));

				double value = bestIndividual.getFitnessFunction()
						.convertToRealValue(bestIndividual.getFitness());
				logArea.append("\nWartość funkcji: " + value);

				OptimizationProblem optimizationProblem = algorithm
						.getEvolutionaryAlghorithmEpochRunners().get(0)
						.getEvolutionaryAlghorithm().getOptimizationProblem();
				if (optimizationProblem.isOptimumValueKnown()) {
					logArea.append("\nOdległość od optimum: "
							+ (value - optimizationProblem.getOptimumValue()));
				}
			}
		});
	}
}
